import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, Canceler } from 'axios'
import { ElLoading, ElMessage } from 'element-plus'
import { RequestEnum } from '@/enum/httpEnum'



export interface IAxiosRequestConfig extends AxiosRequestConfig {
  load?: boolean
}

export interface IAxiosResponse extends AxiosResponse {
  config: IAxiosRequestConfig
}

export default class Axios { 
  private instance: AxiosInstance
  private loading: any

  // Loading
  public requestStart: any
  public requestEnd: any

  // 错误处理
  public requestError: any

  // response
  public dataFactory: any

  constructor(options) {
    this.instance = axios.create({
      load: false,
      abort: false,
      withCredentials: true,
      ...options
    })
    this.interceptors()
  }

  private interceptors() {
    this.interceptorsRequest()
    this.interceptorsResponse()
  }

  private interceptorsRequest() {
    this.instance.interceptors.request.use((config: IAxiosRequestConfig) => {
        if (config?.load) {
          this.loading = this.loading ?? ElLoading.service({
            background: 'rgba(255,255,255,0.1)',
          })
        }
        if (config!.headers) {
          Object.assign(this.instance.defaults.headers, config.headers)
        }
        this.requestStart && this.requestStart(config)
        return config
      },(error: any) => {
        return Promise.reject(error)
      },
    )
  }

  private interceptorsResponse() {
    this.instance.interceptors.response.use(
      (response: IAxiosResponse) => {
        if (response.config?.load) {
          this.loading.close()
        }
        this.requestEnd && this.requestEnd(response.config)
        return this.dataFactory && this.dataFactory(response)
      },(error) => {
        this.loading.close()
        if (error.config) {
          this.requestEnd && this.requestEnd(error.config)
        }
        return this.requestError && this.requestError(error)
      }
    )
  }

  request = <T = any>(url: string, data: any, config: IAxiosRequestConfig): Promise<T> => {
    if (!url) {
      throw new Error('Please configure the correct request parameters!')
    }
    if (config.method?.toUpperCase() === RequestEnum.GET) {
      config.params = Object.assign(data || {})
    } else {
      config.data = Object.assign(data || {})
    }
    config.url = url
    return this.instance.request<T, T>(config)
  }

  /**
   * @description:  get
   * @param url
   * @param data
   * @param config
   */
  get<T = any>(url: string, data?: any, config?: IAxiosRequestConfig): Promise<T> {
    return this.request(url, data, { ...config, method: RequestEnum.GET })
  }

  /**
   * @description:  post
   * @param url
   * @param data
   * @param config
   */
  post<T = any>(url: string, data?: any, config?: IAxiosRequestConfig): Promise<T> {
    return this.request(url, data, { ...config, method: RequestEnum.POST })
  }

  /**
   * @description:  put
   * @param url
   * @param data
   * @param config
   */
  put<T = any>(url: string, data?: any, config?: IAxiosRequestConfig): Promise<T> {
    return this.request(url, data, { ...config, method: RequestEnum.PUT })
  }

  /**
   * @description:  delete
   * @param url
   * @param data
   * @param config
   */
  delete<T = any>(url: string, data?: any, config?: IAxiosRequestConfig): Promise<T> {
    return this.request(url, data, { ...config, method: RequestEnum.DELETE })
  }

  /**
   * @description:  File Upload
   */
  upload<T = any>(url: string, data?: any, config?: IAxiosRequestConfig): Promise<T> {
    return this.instance.request<T, T>({
      url,
      data,
      ...config,
      method: RequestEnum.POST,
      headers: {
        'Content-type': RequestEnum.FORM_DATA,
        ignoreCancelToken: true
      }
    })
  }
}
